/*
 * ANSI C code from the article
 * "Intersecting a Ray with a Cylinder"
 * by Joseph M. Cychosz and Warren N. Waggenspack, Jr.,
 * (3ksnn64@ecn.purdue.edu, mewagg@mewnw.dnet.lsu.edu)
 * in "Graphics Gems IV", Academic Press, 1994
 */

#include	"GraphicsGems.h"
#include	<math.h>
#include        <float.h>

/* ---- intcyl - Intersect a ray with a cylinder. --------------------- */
/*									*/
/*									*/
/*	Description:							*/
/*	    Intcyl determines the intersection of a ray with a		*/
/*	    cylinder.							*/
/*									*/
/*	On entry:							*/
/*	    raybase = The base point of the intersecting ray.		*/
/*	    raycos  = The direction cosines of the above ray. (unit)	*/
/*	    base    = The base location of the cylinder.		*/
/*	    axis    = The axis of symmetry for the cylinder.  (unit)	*/
/*	    radius  = The radius of the cylinder.			*/
/*									*/
/*	On return:							*/
/*	    in	    = The entering distance of the intersection.	*/
/*	    out	    = The leaving  distance of the intersection.	*/
/*									*/
/*	Returns:  True if the ray intersects the cylinder.		*/
/*									*/
/*	Note:	  In and/or out may be negative indicating the		*/
/*		  cylinder is located behind the origin of the ray.	*/
/*									*/
/* -------------------------------------------------------------------- */


boolean intcyl	(raybase,raycos,base,axis,radius,in,out)

	Point3		*raybase;	/* Base of the intersection ray */
	Vector3		*raycos;	/* Direction cosines of the ray */
	Point3		*base;		/* Base of the cylinder		*/
	Vector3		*axis;		/* Axis of the cylinder		*/
	double		radius;		/* Radius of the cylinder	*/
	double		*in;		/* Entering distance		*/
	double		*out;		/* Leaving distance		*/

{
	boolean		hit;		/* True if ray intersects cyl	*/
	Vector3		RC;		/* Ray base to cylinder base	*/
	double		d;		/* Shortest distance between	*/
					/*   the ray and the cylinder	*/
	double		t, s;		/* Distances along the ray	*/
	Vector3		n, D, O;
	double		ln;
#ifndef HUGE
#define HUGE FLT_MAX
#endif
const	double		pinf = HUGE;	/* Positive infinity		*/


	RC.x = raybase->x - base->x;
	RC.y = raybase->y - base->y;
	RC.z = raybase->z - base->z;
	V3Cross (raycos,axis,&n);

	if  ( (ln = V3Length (&n)) == 0. ) {	/* ray parallel to cyl	*/
	    d	 = V3Dot (&RC,axis);
	    D.x	 = RC.x - d*axis->x;
	    D.y	 = RC.y - d*axis->y;
	    D.z	 = RC.z - d*axis->z;
	    d	 = V3Length (&D);
	    *in	 = -pinf;
	    *out =  pinf;
	    return (d <= radius);		/* true if ray is in cyl*/
	}

	V3Normalize (&n);
	d    = fabs (V3Dot (&RC,&n));		/* shortest distance	*/
	hit  = (d <= radius);

	if  (hit) {				/* if ray hits cylinder */
	    V3Cross (&RC,axis,&O);
	    t = - V3Dot (&O,&n) / ln;
	    V3Cross (&n,axis,&O);
	    V3Normalize (&O);
	    s = fabs (sqrt(radius*radius - d*d) / V3Dot (raycos,&O));
	    *in	 = t - s;			/* entering distance	*/
	    *out = t + s;			/* exiting  distance	*/
	}

	return (hit);
}


/* ---- clipobj - Clip object with plane pair. ------------------------ */
/*									*/
/*									*/
/*	Description:							*/
/*	    Clipobj clips the supplied infinite object with two		*/
/*	    (a top and a bottom) bounding planes.			*/
/*									*/
/*	On entry:							*/
/*	    raybase = The base point of the intersecting ray.		*/
/*	    raycos  = The direction cosines of the above ray. (unit)	*/
/*	    bot	    = The normal and perpendicular distance of the	*/
/*		      bottom plane.					*/
/*	    top	    = The normal and perpendicular distance of the	*/
/*		      top plane.					*/
/*	    objin   = The entering distance of the intersection with	*/
/*		      the object.					*/
/*	    objout  = The exiting  distance of the intersection with	*/
/*		      the object.					*/
/*									*/
/*	On return:							*/
/*	    objin   = The entering distance of the intersection.	*/
/*	    objout  = The exiting  distance of the intersection.	*/
/*	    surfin  = The identifier for the entering surface.		*/
/*	    surfout = The identifier for the leaving surface.		*/
/*									*/
/*	Returns:  True if the ray intersects the bounded object.	*/
/*									*/
/* -------------------------------------------------------------------- */

#define		SIDE	0		/* Object surface		*/
#define		BOT	1		/* Bottom end-cap surface	*/
#define		TOP	2		/* Top	  end-cap surface	*/

typedef struct	{			/* Plane: ax + by + cz + d = 0	*/
	double	a ,b ,c, d;
}	Plane;


boolean clipobj		(raybase,raycos,bot,top,objin,objout,surfin,surfout)

	Point3		*raybase;	/* Base of the intersection ray */
	Vector3		*raycos;	/* Direction cosines of the ray */
	Plane		*bot;		/* Bottom end-cap plane		*/
	Plane		*top;		/* Top end-cap plane		*/
	double		*objin;		/* Entering distance		*/
	double		*objout;	/* Exiting  distance		*/
	int		*surfin;	/* Entering surface identifier	*/
	int		*surfout;	/* Exiting  surface identifier	*/

{
	double	dc, dw, t;
	double	in, out;		/* Object  intersection dists.	*/

	*surfin = *surfout = SIDE;
	in  = *objin;
	out = *objout;

/*	Intersect the ray with the bottom end-cap plane.		*/

	dc = bot->a*raycos->x  + bot->b*raycos->y  + bot->c*raycos->z;
	dw = bot->a*raybase->x + bot->b*raybase->y + bot->c*raybase->z + bot->d;

	if  ( dc == 0.0 ) {		/* If parallel to bottom plane	*/
	    if	( dw >= 0. ) return (FALSE);
	} else {
	    t  = - dw / dc;
	    if	( dc >= 0.0 ) {			    /* If far plane	*/
		if  ( t > in && t < out ) { out = t; *surfout = BOT; }
		if  ( t < in  ) return (FALSE);
	     } else {				    /* If near plane	*/
		if  ( t > in && t < out ) { in	= t; *surfin  = BOT; }
		if  ( t > out ) return (FALSE);
	    }
	}

/*	Intersect the ray with the top end-cap plane.			*/

	dc = top->a*raycos->x  + top->b*raycos->y  + top->c*raycos->z;
	dw = top->a*raybase->x + top->b*raybase->y + top->c*raybase->z + top->d;

	if  ( dc == 0.0 ) {		/* If parallel to top plane	*/
	    if	( dw >= 0. ) return (FALSE);
	} else {
	    t  = - dw / dc;
	    if	( dc >= 0.0 ) {			    /* If far plane	*/
		if  ( t > in && t < out ) { out = t; *surfout = TOP; }
		if  ( t < in  ) return (FALSE);
	     } else {				    /* If near plane	*/
		if  ( t > in && t < out ) { in	= t; *surfin  = TOP; }
		if  ( t > out ) return (FALSE);
	    }
	}

	*objin	= in;
	*objout = out;
	return (in < out);
}
